package com.hengshan.common.aop;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.hengshan.common.annotation.SystemLog;
import com.hengshan.common.utils.SecurityUtils;
import com.hengshan.common.utils.ServletUtils;
import com.hengshan.domain.event.OperateEvent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

@Aspect
@Component
@Slf4j
public class LogAspect {

    //匹配指定类所有方法切入 execution(* com.hengshan.controller.UserController.*(..))
    //匹配指定目录所有类所有方法切入 execution(* com.hengshan.controller..*.*(..))
    //在带有指定注解的方法进行切入 @annotation(com.hengshan.common.annotation.SystemLog)
    @Around(value = "@annotation(com.hengshan.common.annotation.SystemLog)")
    public Object printLog(ProceedingJoinPoint joinPoint) {
        Object ret;
        try {
            Object[] args = joinPoint.getArgs();
            //获取被增强方法上的方法名，类名，参数信息，注解对象
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            SystemLog systemLog = methodSignature.getMethod().getAnnotation(SystemLog.class);
            String methodName = methodSignature.getName();
            String className = methodSignature.getDeclaringTypeName();
            String[] paramNames = methodSignature.getParameterNames();
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            String method = request.getMethod();
            String contentType = request.getContentType();
            String paramInfo = "";
            List<Object> params = Arrays.stream(args).filter(o -> !(o instanceof HttpServletResponse || o instanceof HttpServletRequest)).collect(Collectors.toList());
            if(null!=contentType && contentType.contains("multipart/form-data; boundary=")){
                MultipartFile file = (MultipartFile) params.get(0);
                paramInfo = file.getName()+":(binary)"+file.getOriginalFilename();
            } else {
                if (method.equals("POST") || method.equals("PUT")) {
                    paramInfo = params.get(0).toString();
                } else {
                    Map<String, Object> map = new HashMap<>();
                    for (int i = 0; i < params.size(); i++) {
                        Object paramValue = args[i];
                        if (Objects.nonNull(paramValue)) {
                            map.put(paramNames[i], paramValue);
                        }
                    }
                    paramInfo = map.toString();
                }
            }
            log.info("======Start======");
            // 打印描述信息
            log.info("BusinessInfo  : {}", systemLog.title() + "--" + systemLog.businessType().getDescribe());
            // 打印请求 URL
            log.info("URL           : {}", request.getRequestURL());
            // 打印请求的 IP
            log.info("IP            : {}", request.getRemoteHost());
            // 打印 Http method
            log.info("HTTP Method   : {}", request.getMethod());
            // 打印调用 controller 的全路径以及执行方法
            log.info("Class Method  : {}.{}", className, methodName);
            // 打印请求入参
            log.info("Request Args  : {}", paramInfo);
            ret = joinPoint.proceed();
            // 打印出参
            // log.info("Response      : {}", JSON.toJSONString(ret));
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException("执行接口异常 " + e.getMessage());
        }
        return ret;
    }

    /**
     * 处理完请求后执行
     */
    @AfterReturning(value = "@annotation(systemLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, SystemLog systemLog, Object jsonResult) {
        handleLog(joinPoint, systemLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     */
    @AfterThrowing(value = "@annotation(systemLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, SystemLog systemLog, Exception e) {
        handleLog(joinPoint, systemLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, SystemLog systemLog, final Exception exception, Object jsonResult) {
        // 操作日志事件
        OperateEvent operateEvent = new OperateEvent();
        operateEvent.setStatus(0);
        operateEvent.setIp(ServletUtils.getClientIP());
        operateEvent.setUrl(ServletUtils.getRequest().getRequestURI());
        operateEvent.setUsername(SecurityUtils.getLoginUser().getUsername());
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        operateEvent.setMethod(className + "." + methodName + "()");
        operateEvent.setRequestMethod(ServletUtils.getRequest().getMethod());
        operateEvent.setBusinessType(systemLog.businessType().getType());
        operateEvent.setTitle(systemLog.title());
        Object[] args = joinPoint.getArgs();
        List<Object> collect = Arrays.stream(args).filter(o -> !(o instanceof HttpServletResponse || o instanceof HttpServletRequest)).collect(Collectors.toList());
        operateEvent.setParam(JSON.toJSONString(collect));
        if (exception != null) {
            operateEvent.setStatus(1);
            operateEvent.setErrorMessage(exception.getMessage());
        }
        // 发布事件
        SpringUtil.getApplicationContext().publishEvent(operateEvent);
    }
}
